package com.sec.android.app.myfiles.module.local.foldertree;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteFullException;
import android.net.Uri;

import com.sec.android.app.myfiles.feature.CloudMgr;
import com.sec.android.app.myfiles.feature.PrivateModeMgr;
import com.sec.android.app.myfiles.feature.cloud.AbsCloudImp;
import com.sec.android.app.myfiles.info.AppConstants;
import com.sec.android.app.myfiles.info.FileType;
import com.sec.android.app.myfiles.log.Log;
import com.sec.android.app.myfiles.module.abstraction.AbsProviderImp;
import com.sec.android.app.myfiles.module.abstraction.FileRecord;
import com.sec.android.app.myfiles.module.abstraction.FileRecord.StorageType;
import com.sec.android.app.myfiles.module.cloud.CloudFileRecord;
import com.sec.android.app.myfiles.module.local.category.CategoryFileRecord;
import com.sec.android.app.myfiles.module.local.file.LocalFileDbTableInfo;
import com.sec.android.app.myfiles.module.shortcut.ShortcutDbTableInfo;
import com.sec.android.app.myfiles.module.shortcut.ShortcutFileRecord;
import com.sec.android.app.myfiles.provider.DbTableInfo;
import com.sec.android.app.myfiles.provider.DbTableInfo.COLUMN_ID;
import com.sec.android.app.myfiles.util.PreferenceUtils;
import com.sec.android.app.myfiles.util.StorageMonitor;
import com.sec.android.app.myfiles.util.UiUtils;

import java.util.ArrayList;
import java.util.Collection;

public class FolderTreeProviderImp extends AbsProviderImp {
    public static final String COL_OPEN_PATH_NAME = "open_path";
    public static final String COL_OPENED = "opened";

    public FolderTreeProviderImp(Context context) {
        super(context);
    }

    @Override
    protected void _updateDB(SQLiteDatabase db, int oldVersion, int newVersion) {
        try {
            db.execSQL(getRealTableDropQuery());
            db.execSQL(getRealTableCreateQuery());

            db.execSQL(getViewTableDropQuery());
            db.execSQL(getViewTableCreateQuery());
        } catch (SQLiteFullException e) {
            Log.e(this, "SQLiteFullException:" + e.toString());
        }
    }

    private String getRealTableName() {
        return "opened_folder";
    }

    private String getRealTableDropQuery() {
        return "DROP TABLE IF EXISTS " + getRealTableName();
    }

    private String getRealTableCreateQuery() {
        return "CREATE TABLE " + getRealTableName() + " ( _index INTEGER PRIMARY KEY AUTOINCREMENT,  " + COL_OPEN_PATH_NAME
                + " TEXT UNIQUE COLLATE NOCASE)";
    }

    private String getViewTableCreateQuery() {
        int columnCnt = mDbTableInfo.getColumnCount();
        StringBuilder colDef = new StringBuilder();
        for (int i = 0; i < columnCnt; i++) {
            COLUMN_ID columnId = mDbTableInfo.getColumnID(i);
            String columnName = mDbTableInfo.getColumnName(columnId);

            if (columnId == COLUMN_ID.OPENED)
                continue;// except opened column.

            colDef.append("A.").append(columnName).append(" AS ").append(columnName).append(", ");
        }

        String query = "CREATE VIEW " + mDbTableInfo.getTableName() + " as SELECT " + colDef + "( select count(*) from " + getRealTableName()
                + " where " + COL_OPEN_PATH_NAME + " = A.path || '/'|| a.name) as " + COL_OPENED
                + " , A.path || '/'|| a.name || '/' As fullpath " + "FROM " + LocalFileDbTableInfo.getInstance().getTableName() + " A, "
                + getRealTableName() + " B where A.path = B." + COL_OPEN_PATH_NAME + " AND A.file_type = " + FileType.FOLDER
                + " order by fullpath COLLATE NOCASE";

        return query;
    }

    private String getViewTableDropQuery() {
        return "DROP VIEW IF EXISTS " + mDbTableInfo.getTableName();
    }


    protected Uri _insert(Uri uri, ContentValues values) {
        Uri ret = null;

        long rowId = mDB.insertWithOnConflict(getRealTableName(), null, values, SQLiteDatabase.CONFLICT_REPLACE);

        if (rowId > 0) {
            ret = ContentUris.withAppendedId(Uri.parse(getMyUri()), rowId);
        }

        return ret;
    }

    protected int _delete(String selection, String[] selectionArgs) {
        return mDB.delete(getRealTableName(), selection, selectionArgs);
    }

    @Override
    protected StorageType getStorage() {
        return StorageType.FolderTree;
    }

    @Override
    protected boolean needRefresh(Uri uri, FileRecord curRecord) {
        return false;
    }

    @Override
    protected void _refreshDB(Uri uri, FileRecord curRecord) {
    }

    private ArrayList<FileRecord> getShortCutItem() {
        ArrayList<FileRecord> ret = new ArrayList<>();

        ContentResolver cr = mContext.getContentResolver();
        DbTableInfo tableInfo = ShortcutDbTableInfo.getInstance();
        String selection = tableInfo.getColumnName(COLUMN_ID.SHORTCUT_TYPE) + "=" + FileRecord.ShortcutType.MyFiles.ordinal();

        boolean showHidden = PreferenceUtils.getShowHiddenFiles(mContext);
        if (!showHidden) {
            selection += " AND " + tableInfo.getColumnName(COLUMN_ID.NAME) + " NOT LIKE '.%'";
            selection += " AND " + tableInfo.getColumnName(COLUMN_ID.PATH) + " NOT LIKE '%/.%'";
        }

        try (Cursor c = cr.query(Uri.parse(tableInfo.getUri()), null, selection, null, null)) {
            if (c != null && c.getCount() > 0) {
                c.moveToFirst();

                do {
                    FileRecord record = shortcutRecord(c);
                    if (record != null) {
                        ret.add(record);
                    }
                } while (c.moveToNext());

            }
        }

        return ret;
    }

    private FileRecord shortcutRecord(Cursor c) {
        FileRecord ret = null;

        DbTableInfo tableInfo = ShortcutDbTableInfo.getInstance();

        final int pathIndex = tableInfo.getIndex(DbTableInfo.COLUMN_ID.PATH);
        final int nameIndex = tableInfo.getIndex(DbTableInfo.COLUMN_ID.NAME);
        final int fileTypeIndex = tableInfo.getIndex(DbTableInfo.COLUMN_ID.FILE_TYPE);

        if (c != null) {
            final String path = c.getString(pathIndex);
            final String name = c.getString(nameIndex);
            final int fileType = c.getInt(fileTypeIndex);
            ret = new ShortcutFileRecord(path, name, fileType);
        }

        return ret;
    }

    @Override
    protected Cursor _query(Uri uri, String[] projectionIn, String selection, String[] selectionArgs, String sortOrder) {
        Cursor treeCursor = super._query(uri, projectionIn, selection, selectionArgs, sortOrder);
        MatrixCursor ret = null;

        if (treeCursor != null) {
            String[] column = new String[mDbTableInfo.getColumnCount() + 1];

            for (int i = 0; i < mDbTableInfo.getColumnCount(); i++) {
                column[i] = mDbTableInfo.getColumnName(mDbTableInfo.getColumnID(i));
            }
            column[mDbTableInfo.getColumnCount()] = "storage_type";

            ArrayList<FileRecord> homeItemList = new ArrayList<>();
            homeItemList.add(UiUtils.getRecentRecord());

            // For Category Header
            homeItemList.add(FileRecord.createFileRecord(StorageType.Category, null));
            homeItemList.add(FileRecord.createFileRecord(StorageType.Category, CategoryFileRecord.getCategoryPath(FileRecord.CategoryType.Image)));
            homeItemList.add(FileRecord.createFileRecord(StorageType.Category, CategoryFileRecord.getCategoryPath(FileRecord.CategoryType.Audio)));
            homeItemList.add(FileRecord.createFileRecord(StorageType.Category, CategoryFileRecord.getCategoryPath(FileRecord.CategoryType.Video)));
            homeItemList.add(FileRecord.createFileRecord(StorageType.Category, CategoryFileRecord.getCategoryPath(FileRecord.CategoryType.Document)));
            homeItemList.add(FileRecord.createFileRecord(StorageType.Category, CategoryFileRecord.getCategoryPath(FileRecord.CategoryType.Apk)));
            homeItemList.add(FileRecord.createFileRecord(StorageType.Downloads, AppConstants.StoragePath.DOWNLOADS));

            // For Local Header
            homeItemList.add(FileRecord.createFileRecord(StorageType.FolderTree, null));
            homeItemList.add(FileRecord.createFileRecord(StorageType.Local, AppConstants.StoragePath.INTERNAL_ROOT));

            boolean isExternalStorageAllowed = StorageMonitor.isExternalStorageAvailable(null, mContext);
            if (isExternalStorageAllowed) {
                homeItemList.add(FileRecord.createFileRecord(StorageType.Local, StorageMonitor.getExtSDCardPath()));
            }
            if (PrivateModeMgr.getInstance(mContext).isEnabled()) {
                homeItemList.add(FileRecord.createFileRecord(StorageType.Local, PrivateModeMgr.getInstance(mContext).getRootDir()));
            }
            if (isExternalStorageAllowed) {
                for (int i = StorageMonitor.iStorageType.EXTERNAL_USB_DRIVE_A; i <= StorageMonitor.iStorageType.EXTERNAL_USB_DRIVE_F; i++) {
                    if (StorageMonitor.isUsbStorageMounted(i)) {
                        homeItemList.add(FileRecord.createFileRecord(StorageType.Local, StorageMonitor.getUsbStoragePath(i)));
                    }
                }
            }
            if (PreferenceUtils.getMyFilesShortcutCount(mContext) > 0) {
                ArrayList<FileRecord> shortcutFileRecords = getShortCutItem();
                // For Shortcut Header
                homeItemList.add(FileRecord.createFileRecord(StorageType.Shortcut, null));
                for (FileRecord shortcut : shortcutFileRecords) {
                    homeItemList.add(shortcut);
                }
            }
            if (UiUtils.supportCloudGateway(mContext)) {
                Collection<AbsCloudImp> cloudImpList = CloudMgr.getInstance(mContext).getCloudImpList().values();
                if (!cloudImpList.isEmpty()) {
                    // For Cloud Header
                    homeItemList.add(FileRecord.createFileRecord(StorageType.Cloud, null));
                    for (AbsCloudImp cloudImp : cloudImpList) {
                        String cloudPath = CloudMgr.getInstance(mContext).getStoragePath(cloudImp.getCloudType());
                        if (cloudPath != null) {
                            homeItemList.add(FileRecord.createFileRecord(StorageType.Cloud, cloudPath));
                        }
                    }
                }
            }

            ret = new MatrixCursor(column);

            for (FileRecord homeItem : homeItemList) {
                Object[] obj = recordToMatrix(homeItem);
                if (obj != null && homeItem != null) {
                    if (homeItem.getStorageType() != StorageType.Local) {
                        ret.addRow(obj);
                    } else {
                        ArrayList<Object[]> subFolderList = getSubFolderList(homeItem, treeCursor);
                        if (!subFolderList.isEmpty()) {
                            setOpened(obj);
                        }
                        ret.addRow(obj);
                        for (Object[] subFolder : subFolderList) {
                            ret.addRow(subFolder);
                        }
                    }
                }
            }

            treeCursor.close();
        }

        return ret;
    }

    private ArrayList<Object[]> getSubFolderList(FileRecord record, Cursor c) {
        ArrayList<Object[]> ret = new ArrayList<>();

        if (record != null) {
            c.moveToFirst();
            for (int i = 0; i < c.getCount(); i++) {
                String path = c.getString(1);

                if (path.startsWith(record.getFullPath())) {
                    ret.add(cursorToMatrix(c));
                }
                c.moveToNext();
            }
        }

        return ret;
    }

    private void setOpened(Object[] obj) {
        int openedIdx = mDbTableInfo.getIndex(COLUMN_ID.OPENED);
        if (obj != null && obj.length > openedIdx) {
            if (obj[openedIdx] instanceof Integer) {
                obj[openedIdx] = 1;
            }
        }
    }

    private Object[] cursorToMatrix(Cursor c) {
        Object[] ret = new Object[]{
                c.getInt(mDbTableInfo.getIndex(COLUMN_ID.ID)),
                c.getString(mDbTableInfo.getIndex(COLUMN_ID.PATH)),
                c.getString(mDbTableInfo.getIndex(COLUMN_ID.NAME)),
                c.getLong(mDbTableInfo.getIndex(COLUMN_ID.DATE)),
                c.getInt(mDbTableInfo.getIndex(COLUMN_ID.IS_HIDDEN)),
                c.getInt(mDbTableInfo.getIndex(COLUMN_ID.ITEM_COUNT)),
                c.getInt(mDbTableInfo.getIndex(COLUMN_ID.ITEM_COUNT_HIDDEN)),
                c.getInt(mDbTableInfo.getIndex(COLUMN_ID.OPENED)),
                c.getInt(mDbTableInfo.getIndex(COLUMN_ID.FILE_TYPE)),
                StorageType.FolderTree.ordinal()
        };
        return ret;
    }

    private Object[] recordToMatrix(FileRecord record) {
        Object[] ret;
        if (record != null) {
            if (UiUtils.isShowRecentFilesOff(mContext, record)) {
                return null;
            }

            StorageType storageType = record.getStorageType();
            if (storageType == StorageType.Cloud) {
                if (record instanceof CloudFileRecord) {
                    CloudFileRecord cloudRecord = (CloudFileRecord) record;
                    cloudRecord.setFileId(CloudFileRecord.CLOUD_ROOT);
                }
            }

            if (storageType == StorageType.Local) {
                storageType = StorageType.FolderTree;
            }

            ret = new Object[]{
                    -1,                         //id
                    record.getPath(),           //path
                    record.getName(),           //name
                    record.getDate(),           //date
                    record.getHidden(),         //hidden
                    record.getItemCount(false), //count
                    record.getItemCount(true),  //count_hidden
                    0,                          //opened
                    record.getFileType(),       //file_type
                    storageType.ordinal()       //Storage
            };
        } else {
            ret = new Object[]{
                    -1,                         //id
                    "",                         //path
                    "",                         //name
                    0,                          //date
                    0,                          //hidden
                    0,                          //count
                    0,                          //count_hidden
                    0,                          //opened
                    0,                          //file_type
                    StorageType.None.ordinal()  //Storage
            };
        }
        return ret;
    }
}
